package service

import (
	"github.com/gorilla/websocket"
	"sen-golang-study/go_game_study/hero_story_go_server/common/log"
	"sen-golang-study/go_game_study/hero_story_go_server/common/server_msg"
	"sen-golang-study/go_game_study/hero_story_go_server/gateway_server/internal/domain/entity"
	"sen-golang-study/go_game_study/hero_story_go_server/gateway_server/internal/domain/repository"
	"sync/atomic"
	"time"
)

type SessionService struct {
	sessionRepo      *repository.SessionRepository
	serverConnRepo   *repository.ServerConnRepository
	messageRouter    *entity.MessageRouter
	serverSelector   *entity.ServerSelector
	sessionIDCounter uint32
}

func NewSessionService(
	sessionRepo *repository.SessionRepository,
	serverConnRepo *repository.ServerConnRepository,
	messageRouter *entity.MessageRouter,
	serverSelector *entity.ServerSelector,
) *SessionService {
	return &SessionService{
		sessionRepo:      sessionRepo,
		serverConnRepo:   serverConnRepo,
		messageRouter:    messageRouter,
		serverSelector:   serverSelector,
		sessionIDCounter: 0,
	}
}

// HandleNewConnection 处理新的 WebSocket 连接
func (s *SessionService) HandleNewConnection(conn *websocket.Conn) {
	// 生成唯一的会话ID
	sessionID := atomic.AddUint32(&s.sessionIDCounter, 1)

	// 获取客户端IP地址
	clientIP := conn.RemoteAddr().String()

	// 创建新的用户会话
	session := entity.NewUserSession(sessionID, conn, clientIP)

	// 保存会话到仓储
	err := s.sessionRepo.Save(session)
	if err != nil {
		log.Error("保存会话失败: %+v", err)
		err := conn.Close()
		if err != nil {
			log.Error("断开客户端连接失败: %+v", err)
			return
		}
		return
	}

	log.Info("新客户端连接，sessionID=%d, clientIP=%s", sessionID, clientIP)

	// 启动消息循环
	s.StartMessageLoop(session)

	// 清理会话
	err = s.sessionRepo.Delete(sessionID)
	if err != nil {
		log.Error("清理客户端会话失败: %+v", err)
		return
	}
	log.Info("会话已清理，sessionID=%d", sessionID)
}

func (s *SessionService) StartMessageLoop(session *entity.UserSession) {
	go s.loopSendMsg(session)
	s.loopReadMsg(session)
}

func (s *SessionService) loopSendMsg(session *entity.UserSession) {
	clientMsgChan := session.GetClientMsgChan()
	conn := session.GetConnection()

	for {
		msgObj := <-clientMsgChan
		if msgObj == nil || conn == nil {
			continue
		}

		if err := conn.WriteMessage(websocket.BinaryMessage, msgObj); err != nil {
			log.Error("%+v", err)
		}
	}
}

func (s *SessionService) loopReadMsg(session *entity.UserSession) {
	conn := session.GetConnection()
	if conn == nil {
		return
	}

	session.SetReadLimit(64 * 1024)
	s.loopReadClientMsg(session)

	log.Info("客户端断开连接，向游戏服务器发送断开消息")
	disconnectMsg := &server_msg.InternalServerMsg{
		GatewayId:  0,
		SessionId:  session.GetSessionID(),
		UserId:     session.GetUserID(),
		Disconnect: 1,
		MsgData:    nil,
	}

	disconnectMsgBytes := disconnectMsg.ToByteArray()
	gameServerConn, err := s.serverConnRepo.GetConnection(1001)
	if err != nil {
		log.Error("GetConnection err %+v", err)
		return
	}

	err = gameServerConn.WriteMessage(websocket.BinaryMessage, disconnectMsgBytes)
	if err != nil {
		log.Error("发送断开消息失败 %+v", err)
	}
}

func (s *SessionService) loopReadClientMsg(session *entity.UserSession) {
	counter := 0
	t0 := int64(0)

	for {
		msgType, msgData, err := session.ReadMessage()
		if err != nil {
			log.Error("%v+", err)
			break
		}

		t1 := time.Now().UnixMilli()
		if (t1 - t0) > 1000 {
			t0 = t1
			counter = 0
		}

		if counter >= 16 {
			log.Error("too many messages received")
			continue
		}

		counter++

		log.Info("%v", msgData)
		log.Info("收到客户端消息，开始转发")

		innerMsg := server_msg.InternalServerMsg{
			GatewayId: 0,
			SessionId: session.GetSessionID(),
			UserId:    session.GetUserID(),
			MsgData:   msgData,
		}

		innerMsgByteArray := innerMsg.ToByteArray()

		// 根据消息内容确定职责
		responsibility := s.messageRouter.GetResponsibilityByMsgCode(msgData)
		// 使用负载均衡器选择服务器
		serverInfo, err := s.serverSelector.SelectServer(session.GetUserID(), responsibility)
		if err != nil {
			log.Error("SelectServer err %+v", err)
			continue
		}

		gameServerConn, err := s.serverConnRepo.GetConnection(serverInfo.ServerId)
		if err != nil {
			log.Error("GetConnection err %+v", err)
			continue
		}

		if err = gameServerConn.WriteMessage(msgType, innerMsgByteArray); err != nil {
			log.Error("转发消息失败 %+v", err)
		}
	}
}
